#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "HashTable.h"
#include "SDS.h"
#include "YXConfig.h"
#include "YXLog.h"

YXConfig   yxConfig;
HashTable *hash_table;

static int yx_config_map_key_hash(void *key) {
    const unsigned char *data = key;
    int                  hash = 0;
    for (int i = 0; i < strlen(key); i++) {
        hash = 31 * hash + data[i];
    }
    return hash;
}

static int yx_config_map_key_compare(void *key1, void *key2) { return strcmp(key1, key2); }

static void yx_config_map_key_destroy(void *key) { free(key); }

static void yx_config_map_value_destroy(void *value) { free(value); }

static void *yx_config_map_value_dup(void *value) {
    char *dup = malloc(strlen(value) + 1);
    strcpy(dup, value);

    return dup;
}

static void yx_config_map_put(char *key, void *value) { hash_table_put(hash_table, key, value); }

static int yx_config_map_have(char *key) { return hash_table_have(hash_table, key); }

static void *yx_config_map_get(char *key) { return hash_table_get(hash_table, key); }

static void yx_config_map_remove(char *key) { hash_table_full_remove(hash_table, key); }

static void *yx_config_map_pop(char *key) { return hash_table_pop(hash_table, key); }

static char *yx_config_get_str_property(char *key) { return yx_config_map_get(key); }

static int yx_config_get_int_property(char *key) { return atoi(yx_config_get_str_property(key)); }

/**
 * 获取配置文件中的属性值（会从配置类中删除属性对）, 如果不存在, 则返回默认值
 */
static char *yx_config_choose_str_property(char *key, char *default_value) {
    char *value = yxConfig.getAndRemoveProperty(key);
    if (value == NULL) {
        return default_value;
    }
    return value;
}

static int yx_config_choose_int_property(char *key, int default_value) {
    char *value = yxConfig.getAndRemoveProperty(key);
    if (value == NULL) {
        return default_value;
    }

    int int_value = atoi(value);
    yxConfig.freeProperty(value);
    return int_value;
}

__attribute__((constructor(YX_CONFIG_INIT_PRIORITY))) static void yx_config_init() {
    hash_table               = hash_table_create();
    hash_table->keyHash      = yx_config_map_key_hash;
    hash_table->keyCompare   = yx_config_map_key_compare;
    hash_table->keyDestroy   = yx_config_map_key_destroy;
    hash_table->valueDestroy = yx_config_map_value_destroy;
    hash_table->valueDup     = yx_config_map_value_dup;

    yxConfig.setProperty          = yx_config_map_put;
    yxConfig.haveProperty         = yx_config_map_have;
    yxConfig.getProperty          = yx_config_map_get;
    yxConfig.getStrProperty       = yx_config_get_str_property;
    yxConfig.getIntProperty       = yx_config_get_int_property;
    yxConfig.removeProperty       = yx_config_map_remove;
    yxConfig.getAndRemoveProperty = yx_config_map_pop;
    yxConfig.chooseStrProperty    = yx_config_choose_str_property;
    yxConfig.chooseIntProperty    = yx_config_choose_int_property;
    yxConfig.freeProperty         = free;

    log(YX_LOG_INFO, "YXConfig start to init");
    // 读取配置文件
    FILE *fp = fopen(YX_CONFIG_FIXED_PATH, "r");
    if (fp == NULL) {
        logError("The config file yx.cnf is not found");
        return;
    }
    sds     line = sds_create_empty(YX_CONFIG_LINE_MAX_LENGTH);
    ssize_t read;
    ssize_t line_size;
    int     token_num;
    char    sep = '=';

    while (1) {
        if (fgets(line, YX_CONFIG_LINE_MAX_LENGTH + 1, fp) == NULL)
            break;
        read      = strlen(line);
        line_size = read;
        if (read == YX_CONFIG_LINE_MAX_LENGTH && line[read - 1] != '\n') {
            throw("The line of config file is too long");
        }
        if (line[read - 1] == '\n') {
            line[read - 1] = '\0';
            line_size--;
        }
        if (line[read - 2] == '\r') {
            line[read - 2] = '\0';
            line_size--;
        }
        sds *tokens = sds_split(line, line_size, &sep, 1, &token_num);
        if (token_num != 2) {
            sds_free_split(tokens, token_num);
            throw("The format of config file is wrong");
        }
        yxConfig.setProperty(sds_to_str(tokens[0]), sds_to_str(tokens[1]));
    }
    sds_free(line);
    fclose(fp);
    log(YX_LOG_INFO, "YXConfig init finished");
}